ποΈGitΠ―ΡΠ°ποΈ
Commit 484518195e7dbf980de6b951410f23ebac11c237
Parents : 2d5bcc1
Author : Austin <vidplace7@gmail.com>
Signature : Signature validation error
Date : 2026-05-11T10:44:29-04:00
Committer : GitHub <noreply@github.com>
Date : 2026-05-11T14:44:29Z
Gradle: Add flatpakGradleGenerator task (#5369)
Changes
17 files changed, 348 insertions(+), 3 deletions(-)
Diff
diff --git a/.github/workflows/create-or-promote-release.yml b/.github/workflows/create-or-promote-release.yml
index bbb6868d21..55f0521eff 100644
--- a/.github/workflows/create-or-promote-release.yml
+++ b/.github/workflows/create-or-promote-release.yml
@@ -131,6 +131,7 @@ jobs:
channel: ${{ inputs.channel }}
base_version: ${{ inputs.base_version }}
build_desktop: ${{ inputs.build_desktop }}
+ build_flatpak_src: ${{ inputs.build_desktop }}
secrets: inherit
call-promote-workflow:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a48d765f04..befe991adb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -24,6 +24,11 @@ on:
required: false
type: boolean
default: false
+ build_flatpak_src:
+ description: 'Whether to build the Flatpak sources'
+ required: false
+ type: boolean
+ default: false
secrets:
GSERVICES:
required: true
@@ -339,10 +344,105 @@ jobs:
desktop/build/compose/binaries/main-release/*/*.AppImage
desktop/build/compose/jars/*-release.jar
+ create-flatpak-src:
+ if: ${{ inputs.build_flatpak_src }}
+ runs-on: ${{ matrix.os }}
+ needs: [prepare-build-info]
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-22.04, ubuntu-22.04-arm]
+ env:
+ GRADLE_CACHE_URL: ${{ secrets.GRADLE_CACHE_URL }}
+ GRADLE_CACHE_USERNAME: ${{ secrets.GRADLE_CACHE_USERNAME }}
+ GRADLE_CACHE_PASSWORD: ${{ secrets.GRADLE_CACHE_PASSWORD }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+ with:
+ ref: ${{ inputs.tag_name }}
+ fetch-depth: 0
+ submodules: 'recursive'
+
+ - name: Gradle Setup
+ uses: ./.github/actions/gradle-setup
+ with:
+ gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
+ cache_read_only: 'true'
+
+ - name: Python Setup
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.x'
+
+ - name: Install ast-grep
+ run: pip install ast-grep-cli
+
+ # Remove Android/iOS targets and other non-desktop bits
+ # that would break the flatpakGradleGenerator
+ - name: Prepare Offline Desktop Build
+ run: ./scripts/desktop-only-prep.sh
+
+ - name: Generate Flatpak Sources
+ env:
+ DESKTOP_ONLY: true
+ run: >
+ ./gradlew :build-logic:convention:flatpakGradleGenerator flatpakGradleGenerator
+ --no-configuration-cache --refresh-dependencies --no-parallel
+
+ - name: List Flatpak source files
+ run: ls -R flatpak-sources*.json
+
+ - name: Upload Flatpak source artifacts
+ if: always()
+ uses: actions/upload-artifact@v7
+ with:
+ name: flatpak-multisrc-${{ runner.arch }}
+ path: flatpak-sources*.json
+ retention-days: 1
+
+ release-flatpak-src:
+ if: ${{ inputs.build_flatpak_src }}
+ runs-on: ubuntu-24.04
+ needs: [create-flatpak-src]
+ steps:
+ - name: Download Flatpak source artifacts
+ uses: actions/download-artifact@v8
+ with:
+ pattern: flatpak-multisrc-*
+ merge-multiple: true
+
+ - name: List Flatpak source files
+ run: ls -R flatpak-sources*.json
+
+ - name: Combine Flatpak source files
+ run: >
+ jq -s 'add | unique_by(.dest + "/" + .["dest-filename"])' flatpak-sources*.json
+ > flatpak-sources.json
+
+ - name: Upload combined Flatpak source artifact
+ if: always()
+ uses: actions/upload-artifact@v7
+ with:
+ name: flatpak-sources
+ path: flatpak-sources.json
+ retention-days: 1
+
+ - name: Attest combined Flatpak source artifact provenance
+ if: success()
+ uses: actions/attest@v4
+ with:
+ subject-path: flatpak-sources.json
+
github-release:
if: ${{ !cancelled() && !failure() }}
runs-on: ubuntu-24.04-arm
- needs: [prepare-build-info, release-google, release-fdroid, release-desktop]
+ needs:
+ - prepare-build-info
+ - release-google
+ - release-fdroid
+ - release-desktop
+ - release-flatpak-src
env:
INTERNAL_BUILDS_HOST: ${{ secrets.INTERNAL_BUILDS_HOST }}
permissions:
@@ -360,6 +460,9 @@ jobs:
with:
path: ./artifacts
+ - name: Exclude flatpak-multisrc artifacts from release
+ run: rm -rf ./artifacts/flatpak-multisrc-*
+
- name: Create or Update GitHub Release
uses: softprops/action-gh-release@v3
with:
diff --git a/.github/workflows/reusable-check.yml b/.github/workflows/reusable-check.yml
index 9b2af13c02..d1c65e626a 100644
--- a/.github/workflows/reusable-check.yml
+++ b/.github/workflows/reusable-check.yml
@@ -15,6 +15,9 @@ on:
run_desktop_builds:
type: boolean
default: true
+ run_desktop_flatpak_src:
+ type: boolean
+ default: true
upload_artifacts:
type: boolean
default: true
@@ -490,3 +493,62 @@ jobs:
name: desktop-app-${{ runner.os }}-${{ runner.arch }}
path: desktop/build/compose/binaries/main/app/
retention-days: 7
+
+ # ββ Flatpak Sources βββββββββββββββββββββββββββββββββββββββββββββββββββ
+ build-flatpak-src:
+ name: Generate Flatpak Sources (${{ matrix.os }})
+ if: inputs.run_desktop_flatpak_src == true
+ runs-on: ${{ matrix.os }}
+ permissions:
+ contents: read
+ timeout-minutes: 60
+ needs: lint-check
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-24.04, ubuntu-24.04-arm]
+ env:
+ VERSION_CODE: ${{ needs.lint-check.outputs.version_code }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 1
+ submodules: true
+
+ - name: Gradle Setup
+ uses: ./.github/actions/gradle-setup
+ with:
+ gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
+ cache_read_only: true
+
+ - name: Python Setup
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.x'
+
+ - name: Install ast-grep
+ run: pip install ast-grep-cli
+
+ # Remove Android/iOS targets and other non-desktop bits
+ # that would break the flatpakGradleGenerator
+ - name: Prepare Offline Desktop Build
+ run: ./scripts/desktop-only-prep.sh
+
+ - name: Generate Flatpak Sources
+ env:
+ DESKTOP_ONLY: true
+ run: >
+ ./gradlew :build-logic:convention:flatpakGradleGenerator flatpakGradleGenerator
+ --no-configuration-cache --refresh-dependencies --no-parallel
+
+ - run: ls -lah flatpak-sources*.json
+
+ - name: Upload Flatpak Sources
+ if: ${{ inputs.upload_artifacts }}
+ uses: actions/upload-artifact@v7
+ with:
+ name: flatpak-sources-${{ runner.arch }}
+ path: flatpak-sources*.json
+ retention-days: 7
diff --git a/.gitignore b/.gitignore
index 7134def4da..eb9d27ce49 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,3 +60,6 @@ firebase-debug.log
/coil/
/kable/
.opencode/
+
+# flatpakGradleGenerator output
+flatpak-sources*.json
diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts
index c53b86c313..09c00abb79 100644
--- a/build-logic/convention/build.gradle.kts
+++ b/build-logic/convention/build.gradle.kts
@@ -21,6 +21,7 @@ plugins {
`kotlin-dsl`
alias(libs.plugins.spotless)
alias(libs.plugins.detekt)
+ alias(libs.plugins.flatpak.gradle.generator)
}
group = "org.meshtastic.buildlogic"
@@ -96,6 +97,12 @@ detekt {
source.setFrom(files("src/main/java", "src/main/kotlin"))
}
+tasks.flatpakGradleGenerator {
+ outputFile = file("../../flatpak-sources-convention.json")
+ downloadDirectory.set("./offline-repository")
+ excludeConfigurations.set(listOf("testCompileClasspath", "testRuntimeClasspath"))
+}
+
gradlePlugin {
plugins {
register("androidApplication") {
diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts
index ce203e4c94..578efa1057 100644
--- a/build-logic/settings.gradle.kts
+++ b/build-logic/settings.gradle.kts
@@ -26,6 +26,7 @@ pluginManagement {
includeGroupByRegex("com\\.github\\..*")
}
}
+ maven { url = uri("../offline-repository") }
}
}
@@ -51,6 +52,7 @@ dependencyResolutionManagement {
includeGroupByRegex("com\\.github\\..*")
}
}
+ maven { url = uri("../offline-repository") }
}
versionCatalogs {
create("libs") {
diff --git a/build.gradle.kts b/build.gradle.kts
index a839a1b27f..79f18eeec6 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -38,9 +38,23 @@ plugins {
alias(libs.plugins.spotless) apply false
alias(libs.plugins.dokka)
alias(libs.plugins.test.retry) apply false
+ alias(libs.plugins.flatpak.gradle.generator)
alias(libs.plugins.meshtastic.root)
}
dependencies {
dokkaPlugin(libs.dokka.android.documentation.plugin)
}
+
+tasks.flatpakGradleGenerator {
+ outputFile = file("flatpak-sources-root.json")
+ downloadDirectory = "./offline-repository"
+ excludeConfigurations.set(
+ listOf(
+ "dokkaHtmlModuleOutputDirectoriesResolver~internal",
+ "koverExternalArtifacts",
+ "testCompileClasspath",
+ "testRuntimeClasspath",
+ )
+ )
+}
diff --git a/core/ble/build.gradle.kts b/core/ble/build.gradle.kts
index 15cf27b3e1..ac6fe81255 100644
--- a/core/ble/build.gradle.kts
+++ b/core/ble/build.gradle.kts
@@ -18,6 +18,7 @@
plugins {
alias(libs.plugins.meshtastic.kmp.library)
id("meshtastic.koin")
+ alias(libs.plugins.flatpak.gradle.generator)
}
kotlin {
@@ -51,3 +52,20 @@ kotlin {
}
}
}
+
+tasks.flatpakGradleGenerator {
+ outputFile = file("../../flatpak-sources-core-ble.json")
+ downloadDirectory.set("./offline-repository")
+ excludeConfigurations.set(
+ listOf(
+ "androidRuntimeClasspath",
+ "androidCompileClasspath",
+ "androidMainLintChecksClasspath",
+ "androidHostTestCompileClasspath",
+ "androidHostTestLintChecksClasspath",
+ "androidHostTestRuntimeClasspath",
+ "testCompileClasspath",
+ "testRuntimeClasspath",
+ ),
+ )
+}
diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts
index 1927104b4e..a1bc6261b5 100644
--- a/core/common/build.gradle.kts
+++ b/core/common/build.gradle.kts
@@ -20,6 +20,7 @@ plugins {
alias(libs.plugins.kotlin.parcelize)
id("meshtastic.kmp.jvm.android")
id("meshtastic.koin")
+ alias(libs.plugins.flatpak.gradle.generator)
}
kotlin {
@@ -44,3 +45,17 @@ kotlin {
commonTest.dependencies { implementation(libs.kotlinx.coroutines.test) }
}
}
+
+tasks.flatpakGradleGenerator {
+ outputFile = file("../../flatpak-sources-core-common.json")
+ downloadDirectory.set("./offline-repository")
+ excludeConfigurations.set(
+ listOf(
+ "androidHostTestCompileClasspath",
+ "androidHostTestLintChecksClasspath",
+ "androidHostTestRuntimeClasspath",
+ "testCompileClasspath",
+ "testRuntimeClasspath",
+ ),
+ )
+}
diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts
index 3bb132d44e..e732f5c782 100644
--- a/core/database/build.gradle.kts
+++ b/core/database/build.gradle.kts
@@ -21,6 +21,7 @@ plugins {
alias(libs.plugins.meshtastic.kotlinx.serialization)
alias(libs.plugins.kotlin.parcelize)
id("meshtastic.koin")
+ alias(libs.plugins.flatpak.gradle.generator)
}
kotlin {
@@ -74,6 +75,29 @@ kotlin {
dependencies {
"kspJvm"(libs.androidx.room.compiler)
"kspJvmTest"(libs.androidx.room.compiler)
+ // KSP resolves this via a detached configuration at task execution time,
+ // so we declare it explicitly to ensure offline/Flatpak builds can resolve it.
+ "kspJvm"("com.google.devtools.ksp:symbol-processing-aa-embeddable:${libs.versions.devtools.ksp.get()}")
"kspAndroidHostTest"(libs.androidx.room.compiler)
"kspAndroidDeviceTest"(libs.androidx.room.compiler)
}
+
+tasks.flatpakGradleGenerator {
+ outputFile = file("../../flatpak-sources-core-database.json")
+ downloadDirectory.set("./offline-repository")
+ excludeConfigurations.set(
+ listOf(
+ "androidRuntimeClasspath",
+ "androidMainLintChecksClasspath",
+ "androidHostTestRuntimeClasspath",
+ "androidHostTestLintChecksClasspath",
+ "androidHostTestCompileClasspath",
+ "androidDeviceTestRuntimeClasspath",
+ "androidDeviceTestLintChecksClasspath",
+ "androidDeviceTestCompileClasspath",
+ "androidCompileClasspath",
+ "testCompileClasspath",
+ "testRuntimeClasspath",
+ ),
+ )
+}
diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts
index d115947f44..7517bcef29 100644
--- a/core/model/build.gradle.kts
+++ b/core/model/build.gradle.kts
@@ -21,6 +21,7 @@ plugins {
alias(libs.plugins.kotlin.parcelize)
id("meshtastic.kmp.jvm.android")
id("meshtastic.publishing")
+ alias(libs.plugins.flatpak.gradle.generator)
}
kotlin {
@@ -72,3 +73,23 @@ publishing {
}
}
}
+
+tasks.flatpakGradleGenerator {
+ outputFile = file("../../flatpak-sources-core-model.json")
+ downloadDirectory.set("./offline-repository")
+ excludeConfigurations.set(
+ listOf(
+ "androidRuntimeClasspath",
+ "androidMainLintChecksClasspath",
+ "androidHostTestRuntimeClasspath",
+ "androidHostTestLintChecksClasspath",
+ "androidHostTestCompileClasspath",
+ "androidDeviceTestRuntimeClasspath",
+ "androidDeviceTestLintChecksClasspath",
+ "androidDeviceTestCompileClasspath",
+ "androidCompileClasspath",
+ "testCompileClasspath",
+ "testRuntimeClasspath",
+ ),
+ )
+}
diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts
index 378574b1df..bd7237a16d 100644
--- a/core/navigation/build.gradle.kts
+++ b/core/navigation/build.gradle.kts
@@ -19,6 +19,7 @@ plugins {
alias(libs.plugins.meshtastic.kmp.library)
alias(libs.plugins.meshtastic.kmp.library.compose)
alias(libs.plugins.meshtastic.kotlinx.serialization)
+ alias(libs.plugins.flatpak.gradle.generator)
}
kotlin {
@@ -36,3 +37,23 @@ kotlin {
commonTest.dependencies { implementation(projects.core.testing) }
}
}
+
+tasks.flatpakGradleGenerator {
+ outputFile = file("../../flatpak-sources-core-navigation.json")
+ downloadDirectory.set("./offline-repository")
+ excludeConfigurations.set(
+ listOf(
+ "androidRuntimeClasspath",
+ "androidMainLintChecksClasspath",
+ "androidHostTestRuntimeClasspath",
+ "androidHostTestLintChecksClasspath",
+ "androidHostTestCompileClasspath",
+ "androidDeviceTestRuntimeClasspath",
+ "androidDeviceTestLintChecksClasspath",
+ "androidDeviceTestCompileClasspath",
+ "androidCompileClasspath",
+ "testCompileClasspath",
+ "testRuntimeClasspath",
+ ),
+ )
+}
diff --git a/core/proto/build.gradle.kts b/core/proto/build.gradle.kts
index ad0a01a967..d6d5892c96 100644
--- a/core/proto/build.gradle.kts
+++ b/core/proto/build.gradle.kts
@@ -19,6 +19,7 @@ plugins {
alias(libs.plugins.meshtastic.kmp.library)
alias(libs.plugins.wire)
id("meshtastic.publishing")
+ alias(libs.plugins.flatpak.gradle.generator)
}
kotlin {
@@ -61,3 +62,9 @@ publishing {
}
}
}
+
+tasks.flatpakGradleGenerator {
+ outputFile = file("../../flatpak-sources-core-proto.json")
+ downloadDirectory.set("./offline-repository")
+ excludeConfigurations.set(listOf("testCompileClasspath", "testRuntimeClasspath"))
+}
diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts
index 4fa09179f4..42b8c241ee 100644
--- a/desktop/build.gradle.kts
+++ b/desktop/build.gradle.kts
@@ -30,6 +30,7 @@ plugins {
alias(libs.plugins.meshtastic.koin)
id("meshtastic.kover")
id("meshtastic.aboutlibraries")
+ alias(libs.plugins.flatpak.gradle.generator)
}
configureGraphTasks()
@@ -322,3 +323,12 @@ dependencies {
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(kotlin("test"))
}
+
+// Must be run on each architecture to generate separate flatpak-sources.
+tasks.flatpakGradleGenerator {
+ val arch = System.getProperty("os.arch").let { if (it == "amd64") "x86_64" else it }
+ outputFile = file("../flatpak-sources-desktop-$arch.json")
+ downloadDirectory.set("./offline-repository")
+ onlyArches = arch
+ excludeConfigurations.set(listOf("testCompileClasspath", "testRuntimeClasspath"))
+}
diff --git a/feature/messaging/build.gradle.kts b/feature/messaging/build.gradle.kts
index 7060578b9f..3a1c79a1a1 100644
--- a/feature/messaging/build.gradle.kts
+++ b/feature/messaging/build.gradle.kts
@@ -15,7 +15,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-plugins { alias(libs.plugins.meshtastic.kmp.feature) }
+plugins {
+ alias(libs.plugins.meshtastic.kmp.feature)
+ alias(libs.plugins.flatpak.gradle.generator)
+}
kotlin {
android {
@@ -56,3 +59,34 @@ kotlin {
jvmTest.dependencies { implementation(compose.desktop.currentOs) }
}
}
+
+// Gradle's KMP variant resolution follows `available-at` redirects in module
+// metadata and needs android variant `.module` files for disambiguation, even
+// when building desktop-only offline. The androidCompileClasspath configuration
+// can't be resolved by the flatpak generator due to AGP variant ambiguity on
+// project dependencies, so we capture android KMP metadata via a dedicated
+// configuration that only holds external dependencies.
+val flatpakKmpAndroidMeta by
+ configurations.creating {
+ isCanBeResolved = true
+ isCanBeConsumed = false
+ }
+
+dependencies { flatpakKmpAndroidMeta("androidx.paging:paging-compose-android:${libs.versions.paging.get()}") }
+
+tasks.flatpakGradleGenerator {
+ outputFile = file("../../flatpak-sources-feature-messaging.json")
+ downloadDirectory.set("./offline-repository")
+ excludeConfigurations.set(
+ listOf(
+ "androidRuntimeClasspath",
+ "androidMainLintChecksClasspath",
+ "androidCompileClasspath",
+ "androidHostTestCompileClasspath",
+ "androidHostTestLintChecksClasspath",
+ "androidHostTestRuntimeClasspath",
+ "testCompileClasspath",
+ "testRuntimeClasspath",
+ ),
+ )
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 8158804201..89443e66df 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -300,6 +300,7 @@ wire = { id = "com.squareup.wire", version.ref = "wire" }
room = { id = "androidx.room3", version.ref = "room" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
test-retry = { id = "org.gradle.test-retry", version.ref = "testRetry" }
+flatpak-gradle-generator = { id = "io.github.jwharm.flatpak-gradle-generator", version = "1.7.0" }
# Meshtastic
meshtastic-android-application = { id = "meshtastic.android.application" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 185a8a65a8..9235220424 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -18,10 +18,11 @@
pluginManagement {
includeBuild("build-logic")
repositories {
- gradlePluginPortal()
google()
mavenCentral()
+ gradlePluginPortal()
maven { url = uri("https://jitpack.io") }
+ maven { url = uri("./offline-repository") }
}
}
@@ -55,6 +56,7 @@ dependencyResolutionManagement {
includeGroupByRegex("com\\.github\\..*")
}
}
+ maven { url = uri("./offline-repository") }
}
}
Served by rngit 1.5.0 - Generated in 0.19s